package tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Iterator;

import org.junit.Test;

import coreAlgorithm.Bit;
import coreAlgorithm.ID;



public class IDTest {
	ID idMin = new ID("");
	ID idMax = new ID("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");

	@Test
	public void testXORDistanceIDID() {
	}

	@Test
	public void testEquals() {
		assertEquals(new ID("0"), new ID("00"));
		assertTrue(new ID("1").equals(new ID("001")));
		assertEquals(new ID("101101"), new ID("101101"));
		assertEquals(new ID("111001"), new ID(57));
		assertFalse(new ID(9).equals("Not an ID"));
	}

	@Test
	public void testGetBitOnPosition() {
		ID id1 = new ID("111000");
		assertEquals(0, id1.getValueOnPosition(0));
		assertEquals(0, id1.getValueOnPosition(159));
		assertEquals(1, id1.getValueOnPosition(156));
		try {
			assertEquals(id1.getValueOnPosition(160), 0);
			fail("Could fetch an index of of bounds!");
		} catch (IndexOutOfBoundsException e) {
			// wanted behaviour
		}
		try {
			assertEquals(id1.getValueOnPosition(-1), 0);
			fail("Could fetch an index of of bounds!");
		} catch (IndexOutOfBoundsException e) {
			// wanted behaviour
		}

		ID id2 = new ID("1010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010");
		assertEquals(1, id2.getValueOnPosition(0));
		assertEquals(0, id2.getValueOnPosition(1));
		assertEquals(1, id2.getValueOnPosition(158));
		assertEquals(0, id2.getValueOnPosition(159));

		try {
			assertEquals(id2.getValueOnPosition(160), 0);
			fail("Could fetch an index of of bounds!");
		} catch (IndexOutOfBoundsException e) {
			// wanted behaviour
		}
		try {
			assertEquals(id2.getValueOnPosition(-1), 0);
			fail("Could fetch an index of of bounds!");
		} catch (IndexOutOfBoundsException e) {
			// wanted behaviour
		}

		ID id3 = new ID("");
		assertEquals(0, id3.getValueOnPosition(159));

	}

	@Test
	public void testXORDistanceID() {
		ID id1 = new ID("101010");
		ID id1Opposite = new ID("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010101");

		ID id2 = new ID("10101");
		ID id3 = new ID("110011");
		
		assertEquals(new ID("111111"), ID.applyXOR(id1, id2));
		assertEquals(idMin, ID.SmallestID);
		assertEquals(idMax, ID.LargestID);

		assertEquals(id1, id1.applyXOR(ID.SmallestID));
		assertEquals(id1Opposite, id1.applyXOR(ID.LargestID));
		assertEquals(id2.applyXOR(ID.SmallestID), ID.SmallestID.applyXOR(id2));
		assertEquals(ID.SmallestID, id2.applyXOR(id2));
		
		//Triangle property d(x,y) + d(y,z) >= d(x,z)
		assertEquals(ID.applyXOR(id1, id3), ID.applyXOR(ID.applyXOR(id1, id2), ID.applyXOR(id2, id3)));

	}
	
	@Test
	public void testPositionOfMostSignificantBit() {
		assertEquals(ID.maxLength-1, idMax.getRightOffsetOfMostSignificantBit());
		assertEquals(0, new ID("1").getRightOffsetOfMostSignificantBit());
		assertEquals(0,idMin.getRightOffsetOfMostSignificantBit());

		assertEquals(1, new ID("11").getRightOffsetOfMostSignificantBit());
		assertEquals(1, new ID("10").getRightOffsetOfMostSignificantBit());
		assertEquals(2, new ID("111").getRightOffsetOfMostSignificantBit());
		assertEquals(0, new ID("01").getRightOffsetOfMostSignificantBit());
		assertEquals(2, new ID("101").getRightOffsetOfMostSignificantBit());
		assertEquals(0, new ID("00001").getRightOffsetOfMostSignificantBit());
	}
	

	
	@Test
	public void testIterator() {
		Iterator<Bit> iter;
		
		iter = new ID("10").iteratorOnMostSignificantBit();
		assertEquals(new Bit(1), iter.next());
		assertEquals(new Bit(0), iter.next());
		assertFalse(iter.hasNext());
		

		iter = new ID("0").iteratorOnMostSignificantBit();
		assertEquals(new Bit(0), iter.next());
		assertFalse(iter.hasNext());

		iter = new ID("00").iteratorOnMostSignificantBit();
		assertEquals(new Bit(0), iter.next());
		assertFalse(iter.hasNext());
		

		iter = new ID("101").iteratorOnMostSignificantBit();
		assertEquals(new Bit(1), iter.next());
		assertEquals(new Bit(0), iter.next());
		assertEquals(new Bit(1), iter.next());
		assertFalse(iter.hasNext());
		

		iter = new ID("1111110000011110010101").iterator();
		assertEquals(new Bit(0), iter.next());
		assertTrue(iter.hasNext());
		

		iter = idMax.iterator();
		assertEquals(new Bit(1), iter.next());
		assertTrue(iter.hasNext());
		

		iter = idMax.iterator(ID.maxLength-1);
		assertEquals(new Bit(1), iter.next());
		assertFalse(iter.hasNext());
		

		iter = idMin.iterator(ID.maxLength-1);
		assertEquals(new Bit(0), iter.next());
		assertFalse(iter.hasNext());
		

		iter = new ID("101").iterator(ID.maxLength-1);
		assertEquals(new Bit(1), iter.next());
		assertFalse(iter.hasNext());

		
		iter = new ID("110").iterator(ID.maxLength-2);
		assertEquals(new Bit(1), iter.next());
		assertEquals(new Bit(0), iter.next());
		assertFalse(iter.hasNext());
		

		iter = new ID("110").iterator(0);
		assertEquals(new Bit(0), iter.next());
		assertTrue(iter.hasNext());
		

		iter = idMax.iterator(0);
		assertEquals(new Bit(1), iter.next());
		assertTrue(iter.hasNext());
	}
	
	@Test(expected=RuntimeException.class)
	public void testIteratorRemoveException() {
new ID(255).iterator().remove();
	}
	
	@Test
	public void testToString() {
		String prefix = "";//ID: ";
		assertEquals(prefix + "11", new ID("11").toString());
		assertEquals(prefix + "1", new ID("01").toString());
		assertEquals(prefix + "10", new ID("10").toString());
		assertEquals(prefix + "111", new ID("111").toString());
		assertEquals(prefix + "110", new ID("110").toString());
		assertEquals(prefix + "11", new ID("000000000011").toString());
	}
	
	@Test
	public void testIntegerValue() throws Exception {
		for (int i = 0; i <= 260; i++) {

			assertEquals(i, new ID(i).integerValue());
		}
		ID testId = new ID("1010");
		ID testId2 = new ID("01010");
		assertTrue(testId.integerValue() == testId2.integerValue());
		ID testId3 = new ID(10);
		assertEquals(testId.integerValue(),testId3.integerValue());
	}

	@Test
	public void testHashcode() throws Exception {
		ID id0 = new ID(0);
		ID id1 = new ID(1);
		ID id2 = new ID(2);
//		System.out.println(id0.hashCode());
//		System.out.println(id1.hashCode());
//		System.out.println(id2.hashCode());
		assertEquals(new ID(0).hashCode(), id0.hashCode());
		
		//Hard assertions towards the distribution of the hash values - could be completed for more values indeed...
		assertTrue(id0.hashCode() != id1.hashCode());
		assertTrue(id1.hashCode() != id2.hashCode());
		assertTrue(id0.hashCode() != id2.hashCode());
		
	}
}
